﻿using System;
using System.Collections.Generic;
using System.Globalization;
using JTLeigh.Commerce.Spencer.Core.Common;
using ProductFeedbackE = JTLeigh.Commerce.Spencer.Features.ProductFeedback.Common.Entities.ProductFeedback;

namespace JTLeigh.Commerce.Spencer.Features.ProductFeedback.Business.Caches
{

	/// <summary>
	/// An in-memory <see cref="IProductFeedbackCache"/> implementation that caches product 
	/// feedback in memory without expiration, scavenging, etc.
	/// </summary>
	/// <remarks>
	/// <para>
	/// This cache should not be used in production unless you are prepared to restart the service 
	/// host whenever product feedback is updated.  This cache should not be used in multi-server 
	/// environments.
	/// </para>
	/// </remarks>
	public class InMemoryProductFeedbackCache : IProductFeedbackCache
	{

		#region Fields

		private Dictionary<string, Dictionary<Guid, ProductFeedbackE>> _productFeedback;

		#endregion // Fields

		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="InMemoryProductFeedbackCache"/> class.
		/// </summary>
		public InMemoryProductFeedbackCache()
		{
			_productFeedback = new Dictionary<string, Dictionary<Guid, ProductFeedbackE>>();
		}

		#endregion // Constructors

		#region IProductFeedbackCache Members

		/// <summary>
		/// Gets the product feedback for the specified product from the cache.
		/// </summary>
		/// <param name="tenantId">The tenant identifier <see cref="Guid"/> of the tenant whose 
		/// product feedback is to be retrieved.</param>
        /// <param name="catalogId">The catalog identifier <see cref="String"/> of the catalog
        /// that the product whose product feedback are to be retrieved is in.</param>
        /// <param name="productId">The product identifier <see cref="String"/> of the product 
		/// whose product feedback are to be retrieved.</param>
		/// <returns>The <see cref="Dictionary{TKey,TValue}"/> of <see cref="ProductFeedback"/> 
		/// objects for the product from the cache, or <see langword="null" /> if feedback for the
		/// product has not been cached.</returns>
		public Dictionary<Guid, ProductFeedbackE> GetProductFeedback(Guid tenantId, string catalogId, string productId)
		{
			string key;
			Dictionary<Guid, ProductFeedbackE> productFeedback;

            Safety.AssertArgumentNotNull("catalogId", catalogId);
            Safety.AssertArgumentNotNull("productId", productId);

			key = ComputeProductKey(tenantId, catalogId, productId);
			_productFeedback.TryGetValue(key, out productFeedback);
			return productFeedback;
		}

		/// <summary>
		/// Sets the product feedback for the specified product in the cache.
		/// </summary>
		/// <param name="tenantId">The tenant identifier <see cref="Guid"/> of the tenant whose 
		/// product feedback is to be stored.</param>
        /// <param name="catalogId">The catalog identifier <see cref="String"/> of the catalog
        /// whose product feedback is to be stored.</param>
        /// <param name="productId">The product identifier <see cref="String"/> of the product 
        /// whose product feedback is to be stored.</param>
        /// <param name="feedback">The <see cref="Dictionary{TKey,TValue}"/> of 
		/// <see cref="ProductFeedback"/> objects to store.</param>
		public void SetProductFeedback(Guid tenantId, string catalogId, string productId, Dictionary<Guid, ProductFeedbackE> feedback)
		{
			string key;

            Safety.AssertArgumentNotNull("catalogId", catalogId);
            Safety.AssertArgumentNotNull("productId", productId);
            Safety.AssertArgumentNotNull("feedback", feedback);

			key = ComputeProductKey(tenantId, catalogId, productId);
			_productFeedback[key] = feedback;
		}

		#endregion // IProductFeedbackCache Members

		#region Implementation

		private static string ComputeProductKey(Guid tenantId, string catalogId, string productId)
		{
            Safety.AssertArgumentNotNull("catalogId", catalogId);
            Safety.AssertArgumentNotNull("productId", productId);

			return String.Concat(tenantId.ToString("D", CultureInfo.InvariantCulture), ":", catalogId, ":", productId);
		}

		#endregion // Implementation

	}

}
